#include "xlcomm/net/detail/sockets_ops.h"

#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include "xlcomm/base/logger.h"

namespace xlcomm {
namespace net {
namespace sockets {

int CreateNonblockSockOrDie() {
  int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
                        IPPROTO_TCP);
  if (sockfd < 0) {
    LOG_SYSFATAL(socket);
  }
  return sockfd;
}

int GetSocketError(int sockfd) {
  int optval;
  socklen_t optlen = static_cast<socklen_t>(sizeof optval);
  if (::getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) {
    return errno;
  } else {
    return optval;
  }
}

struct sockaddr GetLocalAddr(int sockfd) {
  struct sockaddr addr;
  socklen_t len = sizeof(addr);
  MemZero(&addr, sizeof(addr));
  if (::getsockname(sockfd, &addr, &len) < 0) {
    LOG_SYSERROR(getsockname) << "sockfd:" << sockfd;
  }
  return addr;
}

struct sockaddr GetPeerAddr(int sockfd) {
  struct sockaddr addr;
  socklen_t len = sizeof(addr);
  MemZero(&addr, sizeof(addr));
  if (::getpeername(sockfd, &addr, &len) < 0) {
    LOG_SYSERROR(getpeername) << "sockfd:" << sockfd;
  }
  return addr;
}

bool IsSelfConnect(int sockfd) {
  sockaddr local = GetLocalAddr(sockfd);
  sockaddr peer = GetPeerAddr(sockfd);
  sockaddr_in* local_in = reinterpret_cast<sockaddr_in*>(&local);
  sockaddr_in* peer_in = reinterpret_cast<sockaddr_in*>(&peer);
  return (local_in->sin_addr.s_addr == peer_in->sin_addr.s_addr &&
          local_in->sin_port == peer_in->sin_port);
}

ssize_t write(int fd, const void* buf, size_t count) {
  // LOG_TRACE << "fd:" << fd << " before ::write";
  ssize_t n = ::write(fd, buf, count);
  if (n < 0) {
    LOG_SYSERROR(write) << " fd:" << fd;
  }
  LOG_TRACE << "fd:" << fd << " after  ::write, return " << n;
  return n;
}

ssize_t read(int fd, void* buf, size_t count) {
  // LOG_TRACE << "fd:" << fd << " before ::read";
  ssize_t n = ::read(fd, buf, count);
  if (n < 0) {
    LOG_SYSERROR(read) << "fd:" << fd;
  }
  LOG_TRACE << "fd:" << fd << " after  ::read, return " << n;
  return n;
}

ssize_t readv(int fd, const struct iovec* iov, int iovcnt) {
  // LOG_TRACE << "fd:" << fd << " before ::readv";
  ssize_t n = ::readv(fd, iov, iovcnt);
  if (n < 0) {
    LOG_SYSERROR(readv) << "fd:" << fd;
  }
  LOG_TRACE << "fd:" << fd << " after  ::readv, return " << n;
  return n;
}

int close(int fd) {
  int n = ::close(fd);
  if (n < 0) {
    LOG_SYSERROR(close) << "fd:" << fd;
  }
  return n;
}

}  // namespace sockets
}  // namespace net
}  // namespace xlcomm